/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#ifndef GAME_DBC_DBCFILELOADER_H_
#define GAME_DBC_DBCFILELOADER_H_

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif

#include <cstddef>
#include <cstdint>
#include "Shared/Global_Macros.h"
#include "Shared/Util/ByteConverter.h"


namespace Shared {
namespace DBC {

    enum FieldFormat
    {
        DBC_FF_NA = 'x',                                        // ignore/ default, 4 byte size, in Source String means field is ignored, in Dest String means field is filled with default value
        DBC_FF_NA_BYTE = 'X',                                   // ignore/ default, 1 byte size, see above
        DBC_FF_NA_FLOAT = 'F',                                  // ignore/ default,  float size, see above
        DBC_FF_NA_POINTER = 'p',                                // fill default value into dest, pointer size, Use this only with static _data (otherwise mem-leak)
        DBC_FF_STRING = 's',                                    // char*
        DBC_FF_FLOAT = 'f',                                     // float
        DBC_FF_INT = 'i',                                       // std::uint32_t
        DBC_FF_BYTE = 'b',                                      // std::uint8_t
        DBC_FF_SORT = 'd',                                      // sorted by this field, field is not included
        DBC_FF_IND = 'n',                                       // the same,but parsed to _data
        DBC_FF_LOGIC = 'l'                                      // Logical (boolean)
    };

    class DBCFileLoader
    {
    public:
        DBCFileLoader(void);
        ~DBCFileLoader(void);

        bool load(const char* filename, const char* fmt);

        class Record
        {
            public:
                float getFloat(std::size_t field) const
                {
                    SHARED_ASSERT(field < _file._fieldCount);
                    float val = *reinterpret_cast<float*>(_offset + _file.getOffset(field));
                    Shared::Util::endianConvert(val);
                    return val;
                }
                std::uint32_t getUInt(std::size_t field) const
                {
                    SHARED_ASSERT(field < _file._fieldCount);
                    std::uint32_t val = *reinterpret_cast<std::uint32_t*>(_offset + _file.getOffset(field));
                    Shared::Util::endianConvert(val);
                    return val;
                }
                std::uint8_t getUInt8(std::size_t field) const
                {
                    SHARED_ASSERT(field < _file._fieldCount);
                    return *reinterpret_cast<std::uint8_t*>(_offset + _file.getOffset(field));
                }
                const char* getString(std::size_t field) const
                {
                    SHARED_ASSERT(field < _file._fieldCount);
                    std::size_t stringOffset = getUInt(field);
                    SHARED_ASSERT(stringOffset < _file._stringSize);
                    return reinterpret_cast<char*>(_file._stringTable + stringOffset);
                }

            private:
                Record(DBCFileLoader& file, unsigned char* offset): _offset(offset), _file(file) {}

                unsigned char* _offset;
                DBCFileLoader& _file;

                friend class DBCFileLoader;
        };

        Record getRecord(std::size_t id);
        std::uint32_t getNumRows() const { return _recordCount;}
        std::uint32_t getCols() const { return _fieldCount; }
        std::uint32_t getOffset(std::size_t id) const { return (_fieldsOffset != NULL && id < _fieldCount) ? _fieldsOffset[id] : 0; }
        bool isLoaded() {return (_data != NULL);}
        char* autoProduceData(const char* fmt, std::uint32_t& count, char**& indexTable);
        char* autoProduceStrings(const char* fmt, char* dataTable);
        static std::uint32_t getFormatRecordSize(const char* format, std::int32_t* index_pos = NULL);
    private:

        std::uint32_t _recordSize;
        std::uint32_t _recordCount;
        std::uint32_t _fieldCount;
        std::uint32_t _stringSize;
        std::uint32_t* _fieldsOffset;
        unsigned char* _data;
        unsigned char* _stringTable;
    };

}}

#endif // GAME_DBC_DBCFILELOADER_H_
